#include <iostream>
#include <unistd.h>
#include <cstdlib>//c++风格的c头文件
#include <cstdio>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>

#define N 2
#define NUM 1024

using namespace std;


//问题1：为什么不定义一个全局的字符串，这样父子不就都能看到了吗？
//首先需要写时拷贝，而且通信是可以进行动态的通信的，不是仅仅是这种静态的数据

//问题2：往管道文件中读写也是使用的system call，因为管道文件（内存级文件）也是系统提供的，
//      文件的缓冲区也是系统的，系统不相信你，所以往管道里面读写需要使用系统的接口，也就是在系统的监管之下

//问题3：当前过程经过几次拷贝？答案2次
//      子进程调用write将buffer内容写到管道文件相当于从用户缓冲区写到文件（系统）缓冲区，一次拷贝
//      父进程调用read将管道文件内容读到buffer相当于从文件（系统）缓冲区写到用户缓冲区，一次拷贝 

//问题4：子进程写入后休眠1s，但是父进程没有休眠，但是可以看到父进程打印从文件缓冲区读到的信息也是1s间隔的？
//       父进程会和子进程进行协同的，不然父进程不等子进程往缓冲区写入数据就读，可能会读到缓冲区中的垃圾数据，
//       所以简单来说当前父进程读的快，子进程写的慢，父进程需要等子进程，缓冲区没有数据父进程就别读。
//       同样的，如果在一方读的时候另一方立马写就可能覆盖读的内容，可能读上来的内容一半旧一半新，所以写的一方也
//       会等读的一方读完了再写入缓冲区。这些问题也就是多执行流共享的，访问冲突的问题，也交临界资源竞争问题（后面再说）。
//       协同问题见后面同步与互斥。
//       其实父子进程协同的现象可以分为4种情况，见笔记

//child
void Writer(int wfd)
{
    string s="hello,I am child";
    pid_t self=getpid();
    int number=0;

    char buffer[NUM];
    while(true)
    {
        //构建发送字符串
        buffer[0]=0;//字符串清空，只是为了提醒阅读代码的人，我把这个数组当做字符串了

        //字符串格式化，将本该打印到显示器的内容，放到buffer中，sizeof(buffer)不包含字符0，但是格式化之后buffer中会自动有字符0，见笔记解释
        snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),self,number++);
        //cout<<buffer<<endl;

        //发送/写入给父进程，buffer内容写入管道文件
        write(wfd,buffer,strlen(buffer));//strlen(buffer)不用+1，这是向管道文件中写入内容，字符0是c语言的规定，系统文件没有这个规定，所以不用写入字符0，而且字符0在系统文件会被识别为其它内容

        sleep(1);
    }

}

//father
void Reader(int rfd)
{
    char buffer[NUM];
    //int cnt=0;
    while(true)
    {
        buffer[0]=0;
        //管道文件内容读到buffer,读sizeof(buffer)个字节，但read返回的是实际从文件读到的字节数
        ssize_t n=read(rfd,buffer,sizeof(buffer));//sizeof!=strlen
        if(n>0)//写入内容到管道文件的时候就没有写字符0，所以读的时候也不会读到字符0，但是因为buffer要当字符串用，所以buffer最后给上字符0
        {
            buffer[n]=0;//0=='\0'
            cout<<"father get a message["<<getpid()<<"]#"<<buffer<<endl;
        }
        else if(n==0)
        {
            cout<<"father read file done!"<<endl;
            break;//说明读到了文件的结尾，读取完成，break
        }
        else break;//说明read出错了 

        //cnt++;
        //if(cnt>5) break;
    }

}

int main()
{
    int pipefd[N]={0};
    int n=pipe(pipefd);
    if(n<0) return 1;

    // cout<<"pipefd[0]: "<<pipefd[0]<<",pipefd[1]: "<<pipefd[1]<<endl;

    //child->w,father->r
    pid_t id=fork();
    if(id<0) return 2;
    if(id==0)
    {
        //child
        close(pipefd[0]);

        //进行IPC 
        Writer(pipefd[1]);

        close(pipefd[1]);
        exit(0);
    }

    //father
    close(pipefd[1]);

    //进行IPC
    Reader(pipefd[0]);
  
    //父子进程进行通信，保证父进程最后退出
    pid_t rid=waitpid(id,nullptr,0);
    if(rid<0) return 3;  
   
    //最后通信完也可以让父子进程进行通信的端口的文件描述符关掉，也可以不关 
    close(pipefd[0]);
    return 0;
}
